/***********************************************************************
 * demo16x24.c - Arduino demo program for Holtek HT1632 LED driver chip,
 *            As implemented on the Sure Electronics DE-DP016 display board
 *            (16*24 dot matrix LED module.)
 * Nov, 2008 by Bill Westfield ("WestfW")
 *   Copyrighted and distributed under the terms of the Berkeley license
 *   (copy freely, but include this notice of original author.)
 *
 * Dec 2010, FlorinC - adapted for 3216 display
 * Feb 2015, BriegK - transformed and re-used for GMC
 ***********************************************************************/

#include <Arduino.h>
#include "ht1632.h"
#include <avr/pgmspace.h>
#include "font2.h"
// Date and time functions using a DS1307 RTC connected via I2C and Wire lib
#include <Wire.h>
#include "RTClib.h"

#define X_MAX 32
#define Y_MAX 16
#define CHIP_MAX 4 //Four HT1632Cs on one board
#define CLK_DELAY


// possible values for a pixel;
#define BLACK  0
#define GREEN  1
#define RED    2
#define ORANGE 3


#define plot(x,y,v)  ht1632_plot(x,y,v)
#define cls          ht1632_clear


// our own copy of the "video" memory; 64 bytes for each of the 4 screen quarters;
// each 64-element array maps 2 planes:
// indexes from 0 to 31 are allocated for green plane;
// indexes from 32 to 63 are allocated for red plane;
// when a bit is 1 in both planes, it is displayed as orange (green + red);
byte ht1632_shadowram[64][4] = {0};



/*
 * Set these constants to the values of the pins connected to the SureElectronics Module
 */
static const byte ht1632_data = 6;  // Data pin (pin 7 of display connector)
static const byte ht1632_wrclk = 7; // Write clock pin (pin 5 of display connector)
static const byte ht1632_cs = 8;    // Chip Select (pin 1 of display connnector)
static const byte ht1632_clk = 9; // clock pin (pin 2 of display connector)


#define DEMOTIME 10000  // 30 seconds max on each demo is enough.
#define DISPDELAY 40    // Each "display" lasts this long
#define LONGDELAY 1000  // This delay BETWEEN demos

#define STROBEDELAY 4 // Time interval of strobe effect
#define DISPUPDATEDELAY 2 // Time interval for the display update

//**************************************************************************************************
//Function Name: OutputCLK_Pulse
//Function Feature: enable CLK_74164 pin to output a clock pulse
//Input Argument: void
//Output Argument: void
//**************************************************************************************************
void OutputCLK_Pulse(void) //Output a clock pulse
{
  digitalWrite(ht1632_clk, HIGH);
  digitalWrite(ht1632_clk, LOW);
}


//**************************************************************************************************
//Function Name: OutputA_74164
//Function Feature: enable pin A of 74164 to output 0 or 1
//Input Argument: x: if x=1, 74164 outputs high. If x?1, 74164 outputs low.
//Output Argument: void
//**************************************************************************************************
void OutputA_74164(unsigned char x) //Input a digital level to 74164
{
  digitalWrite(ht1632_cs, (x==1 ? HIGH : LOW));
}


//**************************************************************************************************
//Function Name: ChipSelect
//Function Feature: enable HT1632C
//Input Argument: select: HT1632C to be selected
// If select=0, select none.
// If s<0, select all.
//Output Argument: void
//**************************************************************************************************
void ChipSelect(int select)
{
  unsigned char tmp = 0;
  if(select<0) //Enable all HT1632Cs
  {
    OutputA_74164(0);
    CLK_DELAY;
    for(tmp=0; tmp<CHIP_MAX; tmp++)
    {
      OutputCLK_Pulse();
    }
  }
  else if(select==0) //Disable all HT1632Cs
  {
    OutputA_74164(1);
    CLK_DELAY;
    for(tmp=0; tmp<CHIP_MAX; tmp++)
    {
      OutputCLK_Pulse();
    }
  }
  else
  {
    OutputA_74164(1);
    CLK_DELAY;
    for(tmp=0; tmp<CHIP_MAX; tmp++)
    {
      OutputCLK_Pulse();
    }
    OutputA_74164(0);
    CLK_DELAY;
    OutputCLK_Pulse();
    CLK_DELAY;
    OutputA_74164(1);
    CLK_DELAY;
    tmp = 1;
    for( ; tmp<select; tmp++)
    {
      OutputCLK_Pulse();
    }
  }
}


/*
 * ht1632_writebits
 * Write bits (up to 8) to h1632 on pins ht1632_data, ht1632_wrclk
 * Chip is assumed to already be chip-selected
 * Bits are shifted out from MSB to LSB, with the first bit sent
 * being (bits & firstbit), shifted till firsbit is zero.
 */
void ht1632_writebits (byte bits, byte firstbit)
{
  DEBUGPRINT(" ");
  while (firstbit) {
    DEBUGPRINT((bits&firstbit ? "1" : "0"));
    digitalWrite(ht1632_wrclk, LOW);
    if (bits & firstbit) {
      digitalWrite(ht1632_data, HIGH);
    } 
    else {
      digitalWrite(ht1632_data, LOW);
    }
    digitalWrite(ht1632_wrclk, HIGH);
    firstbit >>= 1;
  }
}


/*
 * ht1632_sendcmd
 * Send a command to the ht1632 chip.
 */
static void ht1632_sendcmd (byte chipNo, byte command)
{
  ChipSelect(chipNo);
  ht1632_writebits(HT1632_ID_CMD, 1<<2);  // send 3 bits of id: COMMMAND
  ht1632_writebits(command, 1<<7);  // send the actual command
  ht1632_writebits(0, 1); 	/* one extra dont-care bit in commands. */
  ChipSelect(0);
}


/*
 * ht1632_senddata
 * send a nibble (4 bits) of data to a particular memory location of the
 * ht1632.  The command has 3 bit ID, 7 bits of address, and 4 bits of data.
 *    Select 1 0 1 A6 A5 A4 A3 A2 A1 A0 D0 D1 D2 D3 Free
 * Note that the address is sent MSB first, while the data is sent LSB first!
 * This means that somewhere a bit reversal will have to be done to get
 * zero-based addressing of words and dots within words.
 */
static void ht1632_senddata (byte chipNo, byte address, byte data)
{
  ChipSelect(chipNo);
  ht1632_writebits(HT1632_ID_WR, 1<<2);  // send ID: WRITE to RAM
  ht1632_writebits(address, 1<<6); // Send address
  ht1632_writebits(data, 1<<3); // send 4 bits of data
  ChipSelect(0);
}


void ht1632_setup()
{
  pinMode(ht1632_cs, OUTPUT);
  digitalWrite(ht1632_cs, HIGH); 	/* unselect (active low) */
  pinMode(ht1632_wrclk, OUTPUT);
  pinMode(ht1632_data, OUTPUT);
  pinMode(ht1632_clk, OUTPUT);

  for (int j=1; j<5; j++)
  {
    ht1632_sendcmd(j, HT1632_CMD_SYSDIS);  // Disable system
    ht1632_sendcmd(j, HT1632_CMD_COMS00);
    ht1632_sendcmd(j, HT1632_CMD_MSTMD); 	/* Master Mode */
    ht1632_sendcmd(j, HT1632_CMD_RCCLK);  // HT1632C
    ht1632_sendcmd(j, HT1632_CMD_SYSON); 	/* System on */
    ht1632_sendcmd(j, HT1632_CMD_LEDON); 	/* LEDs on */
    ht1632_sendcmd(j, 0xA0);
  }
  
  for (byte i=0; i<96; i++)
  {
    ht1632_senddata(1, i, 0);  // clear the display!
    ht1632_senddata(2, i, 0);  // clear the display!
    ht1632_senddata(3, i, 0);  // clear the display!
    ht1632_senddata(4, i, 0);  // clear the display!
  }
  delay(LONGDELAY);
}


/*
 * plot a point on the display, with the upper left hand corner
 * being (0,0), and the lower right hand corner being (31, 15);
 * parameter "color" could have one of the 4 values:
 * black (off), red, green or yellow;
 */
void ht1632_plot (byte x, byte y, byte color)
{
  if (x<0 || x>X_MAX || y<0 || y>Y_MAX)
    return;
  
  if (color != BLACK && color != GREEN && color != RED && color != ORANGE)
    return;
  
  byte nChip = 1 + x/16 + (y>7?2:0) ;
  x = x % 16;
  y = y % 8;
  byte addr = (x<<1) + (y>>2);
  byte bitval = 8>>(y&3);  // compute which bit will need set
  switch (color)
  {
    case BLACK:
      // clear the bit in both planes;
      ht1632_shadowram[addr][nChip-1] &= ~bitval;
      ht1632_senddata(nChip, addr, ht1632_shadowram[addr][nChip-1]);
      addr = addr + 32;
      ht1632_shadowram[addr][nChip-1] &= ~bitval;
      ht1632_senddata(nChip, addr, ht1632_shadowram[addr][nChip-1]);
      break;
    case GREEN:
      // set the bit in the green plane and clear the bit in the red plane;
      ht1632_shadowram[addr][nChip-1] |= bitval;
      ht1632_senddata(nChip, addr, ht1632_shadowram[addr][nChip-1]);
      addr = addr + 32;
      ht1632_shadowram[addr][nChip-1] &= ~bitval;
      ht1632_senddata(nChip, addr, ht1632_shadowram[addr][nChip-1]);
      break;
    case RED:
      // clear the bit in green plane and set the bit in the red plane;
      ht1632_shadowram[addr][nChip-1] &= ~bitval;
      ht1632_senddata(nChip, addr, ht1632_shadowram[addr][nChip-1]);
      addr = addr + 32;
      ht1632_shadowram[addr][nChip-1] |= bitval;
      ht1632_senddata(nChip, addr, ht1632_shadowram[addr][nChip-1]);
      break;
    case ORANGE:
      // set the bit in both the green and red planes;
      ht1632_shadowram[addr][nChip-1] |= bitval;
      ht1632_senddata(nChip, addr, ht1632_shadowram[addr][nChip-1]);
      addr = addr + 32;
      ht1632_shadowram[addr][nChip-1] |= bitval;
      ht1632_senddata(nChip, addr, ht1632_shadowram[addr][nChip-1]);
      break;
  }
}


/*
 * ht1632_clear
 * clear the display, and the shadow memory, and the snapshot
 * memory.  This uses the "write multiple words" capability of
 * the chipset by writing all 96 words of memory without raising
 * the chipselect signal.
 */
void ht1632_clear()
{
  char i;
  for (int i=1; i< 5; i++)
  {
    ChipSelect(-1);
    ht1632_writebits(HT1632_ID_WR, 1<<2);  // send ID: WRITE to RAM
    ht1632_writebits(0, 1<<6); // Send address
    for (i = 0; i < 96/2; i++) // Clear entire display
      ht1632_writebits(0, 1<<7); // send 8 bits of data
    ChipSelect(0);

    for (int j=0; j < 64; j++)
      ht1632_shadowram[j][i-1] = 0;
  }
}


void ht1632_putchar(byte x, byte y, char c, byte color=GREEN)
{
  byte dots;
  if (c >= 'A' && c <= 'Z' ||
    (c >= 'a' && c <= 'z') ) {
    c &= 0x1F;   // A-Z maps to 1-26
  } 
  else if (c >= '0' && c <= '9') {
    c = (c - '0') + 27;
  } 
  else if (c == ' ') {
    c = 0; // space
  }
  for (char col=0; col< 6; col++) {
    dots = pgm_read_byte_near(&myfont[c][col]);
    for (char row=0; row < 7; row++) {
      if (dots & (64>>row))   	     // only 7 rows.
        plot(x+col, y+row, color);
      else 
        plot(x+col, y+row, 0);
    }
  }
}

/*
 * Set these constants to the values of the pins connected to the buttons
 */
const int buttonPin1 = 2;
const int buttonPin2 = 3;
const int buttonPin3 = 4;

//Set Clock variables
int clockMode = 0;
int modeButtonState = 0;
int upButtonState = 0;
int downButtonState=0;
int devez = 0;

// create temp timers
int button1Timer = 0;
int strobeTimer = 0;

int buttonState1 = 0;         // variable for reading the pushbutton status
int buttonState2 = 0;         // variable for reading the pushbutton status
int buttonState3 = 0;         // variable for reading the pushbutton status

int OldButtonState1 = 0;         // variable for reading the pushbutton status
int OldButtonState2 = 0;         // variable for reading the pushbutton status
int OldButtonState3 = 0;         // variable for reading the pushbutton status

int oldHour = 999;
int oldMin = 999;
int currentHour;
int currentMin;

int timeChanged = 0;

/*
 * show_clock
 * draw some characters on the display and scroll them vertically.
 */
void affichage (int eurvez, int minutenn, int devez, int clockMode, int timer, int timeChanged)
{

	if(clockMode == 0){	
		displayHour(eurvez);
		displayMinutes(minutenn);
		displayDay(devez);
	} else if (clockMode == 1){
		if (timer < (STROBEDELAY/2)) {
			displayHour(eurvez);
		}
		else {
			displayHour(999);
		}
		if (timeChanged == 1){
			displayMinutes(minutenn);
			displayDay(devez);
		}
	} else if (clockMode == 3){
		if (timer < (STROBEDELAY/2)) {
			displayMinutes(minutenn);
		}
		else {
			displayMinutes(999);
		}
		if (timeChanged == 1){
			displayHour(eurvez);
			displayDay(devez);
		}		
	} else if (clockMode == 5){
		if (timer < (STROBEDELAY/2)) {
			displayDay(devez);
		}
		else {
			displayDay(999);
		}
		if (timeChanged == 1){
			displayMinutes(minutenn);
			displayHour(eurvez);
		}		
	}
}

/*
*
* description: display funtion for the hour element
*/
void displayHour (int eurvez)
{
  char eurvezDegat;
  char eurvezUnan;
  
  if (eurvez != 999) 
  {
	eurvezDegat = (char) ((eurvez/10)+48);
	eurvezUnan = (char) (eurvez - ((eurvez/10)*10)+48);
  
	ht1632_putchar(2, 0, eurvezDegat, GREEN);
	ht1632_putchar(8, 0, eurvezUnan, GREEN);
  }
  else {
	ht1632_putchar(2, 0, 'G', BLACK);
	ht1632_putchar(8, 0, 'G', BLACK);
  }
}

/*
*
* description: display funtion for the minute element
*/
void displayMinutes (int minutenn)
{
  char minutennDegat;
  char minutennUnan;
  
  if (minutenn != 999) 
  {
	minutennDegat = (char) ((minutenn/10)+48);
	minutennUnan = (char) (minutenn - ((minutenn/10)*10)+48);
  
	ht1632_putchar(20, 0, minutennDegat, GREEN);
	ht1632_putchar(26, 0, minutennUnan, GREEN);
  }
  else {
	ht1632_putchar(20, 0, 'G', BLACK);
	ht1632_putchar(26, 0, 'G', BLACK);
  }
}

/*
*
* description: display funtion for the day element
*/
void displayDay (int devez)
{
	if (devez == 0)
	{
		ht1632_putchar(0, 8, 'L', GREEN);
		ht1632_putchar(6, 8, 'U', GREEN);
		ht1632_putchar(12, 8, 'N', GREEN);
		ht1632_putchar(18, 8, 'D', GREEN);
		ht1632_putchar(24, 8, 'I', GREEN);
	} else if (devez == 1)
	{	
		ht1632_putchar(0, 8, 'M', GREEN);
		ht1632_putchar(6, 8, 'A', GREEN);
		ht1632_putchar(12, 8, 'R', GREEN);
		ht1632_putchar(18, 8, 'D', GREEN);
		ht1632_putchar(24, 8, 'I', GREEN);
	} else if (devez == 2)
	{	
		ht1632_putchar(0, 8, 'M', GREEN);
		ht1632_putchar(6, 8, 'E', GREEN);
		ht1632_putchar(12, 8, 'R', GREEN);	
	} else if (devez == 3)
	{	
		ht1632_putchar(0, 8, 'J', GREEN);
		ht1632_putchar(6, 8, 'E', GREEN);
		ht1632_putchar(12, 8, 'U', GREEN);
		ht1632_putchar(18, 8, 'D', GREEN);
		ht1632_putchar(24, 8, 'I', GREEN);		
	} else if (devez == 4)
	{	
		ht1632_putchar(0, 8, 'V', GREEN);
		ht1632_putchar(6, 8, 'E', GREEN);
		ht1632_putchar(12, 8, 'N', GREEN);	
	} else if (devez == 5)
	{	
		ht1632_putchar(0, 8, 'S', GREEN);
		ht1632_putchar(6, 8, 'A', GREEN);
		ht1632_putchar(12, 8, 'M', GREEN);	
		ht1632_putchar(18, 8, 'D', GREEN);
		ht1632_putchar(24, 8, 'I', GREEN);
	} else if (devez == 6)
	{
		ht1632_putchar(0, 8, 'D', GREEN);
		ht1632_putchar(6, 8, 'I', GREEN);
		ht1632_putchar(12, 8, 'M', GREEN);	
	} else if (devez == 999)
	{
		ht1632_putchar(0, 8, 'G', BLACK);
		ht1632_putchar(6, 8, 'G', BLACK);
		ht1632_putchar(12, 8, 'G', BLACK);
		ht1632_putchar(18, 8, 'G', BLACK);
		ht1632_putchar(24, 8, 'G', BLACK);		
	}
}
/***********************************************************************
 * traditional Arduino sketch functions: setup and loop.
 ***********************************************************************/
RTC_DS1307 rtc;
RTC_Millis _rtcSet;

void setup ()  // flow chart from page 17 of datasheet
{
	ht1632_setup();  randomSeed(analogRead(0));
  
	Serial.begin(57600);
	Wire.begin();
	rtc.begin();

	// initialize the pushbuttons pin as an inputs:
	pinMode(buttonPin1, INPUT);
	pinMode(buttonPin2, INPUT);
	pinMode(buttonPin3, INPUT);
  
    //if (! rtc.isrunning()) {
    //Serial.println("RTC is NOT running!");
    // following line sets the RTC to the date & time this sketch was compiled
    //rtc.adjust(DateTime(__DATE__, __TIME__));
    //}
	cls();
	ht1632_putchar(14, 0, 'h', GREEN); 
}


void loop ()
{

	
	//on synchronise le dateTime
	DateTime now = rtc.now();

	// on synchronise le temps subjectif selon les modes
	if (clockMode == 1){
		oldMin = currentMin;
		currentMin = now.minute();
	} else if (clockMode == 3){
		oldHour = currentHour;
		currentHour = now.hour();
	} else if ((clockMode != 1) and (clockMode != 3) and (clockMode != 2) and (clockMode != 4) and (clockMode != 6)) {
		oldMin = currentMin;
		currentMin = now.minute();
		oldHour = currentHour;	
		currentHour = now.hour();
	}
	
	//Sauvegarde des anciens états
	OldButtonState1 = buttonState1;
	OldButtonState2 = buttonState2;
	OldButtonState3 = buttonState3;
	
	// Lecture des états des boutons
	buttonState1 = digitalRead(buttonPin1);
	buttonState2 = digitalRead(buttonPin2);
	buttonState3 = digitalRead(buttonPin3);
	
	//On vérifie pour chaque bouton qu'on est bien sur un front montant
	if (buttonState1 == LOW and OldButtonState1== HIGH) {
		modeButtonState = 1;
	}
	else {
		modeButtonState = 0;
	}
  
	if (buttonState2 == LOW and OldButtonState2== HIGH) {
		upButtonState = 1;
	}
	else {
		upButtonState = 0;
	}
  
	if (buttonState3 == LOW and OldButtonState3== HIGH) {
		downButtonState = 1;
	}
	else {
		downButtonState = 0;
	}
	

	// Réaction du système face à l'appui sur les boutons
	if (modeButtonState == 1) {
      clockMode = clockMode+1;
      if (clockMode > 6) {
        clockMode = 0;
      }
    }
	if (downButtonState == 1) {
		if (clockMode == 1){
			currentHour = currentHour - 1;
			if (currentHour < 0){
				currentHour = 23;
			}
		} else if (clockMode == 3) {
			currentMin = currentMin - 1;
			if (currentMin < 0){
				currentMin = 59;
			}
		} else if (clockMode == 5) {
			devez = devez - 1;
			if (devez < 0){
				devez = 6;
			}
		}	
	}
	if (upButtonState == 1) {
		if (clockMode == 1){
			currentHour = currentHour + 1;
			if (currentHour > 23){
				currentHour = 0;
			}
		} else if (clockMode == 3) {
			currentMin = currentMin + 1;
			if (currentMin > 59){
				currentMin = 0;
			}
		} else if (clockMode == 5) {
			devez = devez + 1;
			if (devez > 6){
				devez = 0;
			}
		}	
	}
    if (clockMode > 0) {
      strobeTimer = strobeTimer+1;
      if (strobeTimer == STROBEDELAY) {
        strobeTimer = 0;
        }
    }
    else {
      strobeTimer = 0;
    }
	
	
	
	//check if time has changed
	if(oldHour != currentHour){
		timeChanged = 1;
        // Mechanisme de mise à jour de la journée
	    if (oldHour == 23 and currentHour == 0){
			devez = devez + 1;
			if (devez > 6){
				devez = 0;
			}  
            Serial.print("devez: ");
            Serial.print(String(devez));
            Serial.print("\n");
        
	  }
	}
	if(oldMin != currentMin){
		timeChanged = 1;
	}

	//Plusieurs nuanaces d'affichages
	if (clockMode == 0)
	{
		if(timeChanged == 1){
			affichage(currentHour,currentMin,devez,clockMode,strobeTimer,timeChanged);
			timeChanged = 0;
		}
	} else if ((clockMode == 2) or (clockMode == 4) or (clockMode == 6))
	{
		rtc.adjust(DateTime(now.year(), now.month(), now.day(), currentHour, currentMin, now.second()));
		affichage(currentHour,currentMin,devez,0,0,0);
		
		clockMode = clockMode + 1;
		if (clockMode > 6) {
			clockMode = 0;
		}
		
	} else {
	
		affichage(currentHour,currentMin,devez,clockMode,strobeTimer,timeChanged);
	}
	
	//Pour le premier cycle, ces variables valent 999, il faut donc les initialser
	if (oldHour == 999){
		oldHour = now.hour();
		currentHour = now.hour();
	}
	if (oldMin == 999) {
		oldMin = now.minute();
		currentMin = now.minute();
	}

	
}

